﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;

namespace DBUtil;

public partial class DBAccess
{
    #region 执行sql语句 ExecuteSql...

    public virtual int ExecuteSql(string sql)
        => ExecuteSql(sql, null, null, []);

    public virtual int ExecuteSql(string sql, IDictionary<string, object> parameters)
        => ExecuteSql(sql, null, null, parameters?.Keys.Select(key => CreatePara(key, parameters[key])).ToArray());

    public virtual int ExecuteSql(string sql, params DbParameter[] parameters)
        => ExecuteSql(sql, null, null, parameters);

    public virtual int ExecuteSql(string sql, CommandType? commandType, int? timeoutSecond, params DbParameter[] parameters)
    {
        Stopwatch st = null;
        Exception ex = null;
        if (Setting.HasMonitor) { st = new Stopwatch(); st.Start(); }
        try
        {
            return RunInCommand(cmd => cmd.ExecuteNonQuery(), sql, commandType, timeoutSecond, parameters);
        }
        catch (Exception e)
        {
            if (Setting.HasMonitor) ex = e;
            throw;
        }
        finally
        {
            if (Setting.HasMonitor)
            {
                if (st.IsRunning) st.Stop();
                Setting.MonitorAction?.Invoke(new MonitorArgument
                {
                    Sql = sql,
                    Exception = ex,
                    TimeSpan = TimeSpan.FromMilliseconds(st.ElapsedMilliseconds)
                }).Wait();
            }
        }
    }
    #endregion

    #region 执行sql语句 ExecuteSql...
    public virtual async Task<int> ExecuteSqlAsync(string sql, CancellationToken cancellationToken = default)
        => await ExecuteSqlAsync(sql, null, null, [], cancellationToken);

    public virtual async Task<int> ExecuteSqlAsync(string sql, IDictionary<string, object> dic, CancellationToken cancellationToken = default)
        => await ExecuteSqlAsync(sql, null, null, dic?.Keys.Select(key => CreatePara(key, dic[key])).ToArray(), cancellationToken);

    public virtual async Task<int> ExecuteSqlAsync(string sql, IEnumerable<DbParameter> parameters, CancellationToken cancellationToken = default)
        => await ExecuteSqlAsync(sql, null, null, parameters, cancellationToken);

    public virtual async Task<int> ExecuteSqlAsync(string sql, CommandType? commandType, int? timeoutSecond, IEnumerable<DbParameter> parameters, CancellationToken cancellationToken = default)
    {
        Stopwatch st = null;
        Exception ex = null;
        if (Setting.HasMonitor) { st = new Stopwatch(); st.Start(); }
        try
        {
            return await RunInCommandAsync(async cmd => await cmd.ExecuteNonQueryAsync(cancellationToken), sql, commandType, timeoutSecond, parameters);
        }
        catch (Exception e)
        {
            if (Setting.HasMonitor) ex = e;
            throw;
        }
        finally
        {
            if (Setting.HasMonitor)
            {
                if (st.IsRunning) st.Stop();
                await Setting.MonitorAction?.Invoke(new MonitorArgument
                {
                    Sql = sql,
                    Exception = ex,
                    TimeSpan = TimeSpan.FromMilliseconds(st.ElapsedMilliseconds)
                });
            }
        }
    }
    #endregion
}
